#region License, Terms and Conditions
//
// Prawks - Copyright (c) 2007 Atif Aziz. All rights reserved.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License as published by the Free
// Software Foundation; either version 2.1 of the License, or (at your option)
// any later version.
//
// This library is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
// details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this library; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
//
#endregion

namespace Prawks.CodeProvisioning
{
    using System;

    using CultureInfo = System.Globalization.CultureInfo;

    /// <summary>
    /// When overriden in a derived class, allows a type to be designated as
    /// the external implementation provider for a class member.
    /// See <see cref="MethodImplAttribute"/> for methods and 
    /// <see cref="PropertyImplAttribute"/> for properties.
    /// </summary>
    
    public abstract class MemberImplAttribute : Attribute
    {
        /// <summary>
        /// Attempts to infer the imeplementation provider type from the attribtue's name.
        /// </summary>
        /// <param name="implSuffix">
        /// Suffix to use during type inferrence.
        /// </param>
        /// <returns>
        /// Returns the implementation provider type inferred.
        /// </returns>
        /// <remarks>
        /// This method can be used to guess the implementation provider type's name
        /// from the attribute's name by doing the following transformation:
        /// <b><i>XXXX</i>Attribute</b> to <b><i>XXXXYYYY</i></b>. 
        /// <i>XXXX</i> is the base name and <i>YYYY</i> is specified by the
        /// <paramref name="implSuffix"/> parameter. If <paramref name="implSuffix"/>
        /// is <b>Impl</b>, then for an attribute called <b>SampleMethodAttribute</b>,
        /// the guessed type will be <b>SampleMethodImpl</b> (assuming it is defined 
        /// in the same namespace and assembly).
        /// </remarks>
        
        protected Type InferImplType(string implSuffix)
        {
            const string standardAttributeSuffix = "Attribute";

            //
            // First check to see if the custom attribute's name ends with the
            // standard suffix of "Attribute". If it does not, then the search is 
            // immediately aborted with an exception.
            //

            Type attributeType = GetType();
            string attributeName = attributeType.FullName;
            string attributeNameSuffix = attributeName.Substring(Math.Max(0, attributeName.Length - standardAttributeSuffix.Length));

            if (string.Compare(attributeNameSuffix, standardAttributeSuffix, true, CultureInfo.InvariantCulture) != 0)
            {
                throw new MemberImplException(
                    string.Format("Implementation type cannot be inferred from {0} because it does not end with the standard {1} suffix.",
                    attributeName, standardAttributeSuffix));
            }

            //
            // Remove the standard attribute suffix and append the 
            // expected implementation suffix and then look for a type by that 
            // name in the same assembly as the attribute.
            //

            string implTypeName = attributeName.Substring(0, attributeName.Length - standardAttributeSuffix.Length);
            implTypeName += implSuffix;
            Type implType = attributeType.Assembly.GetType(implTypeName, false);
                
            if (implType == null)
                return null;
                    
            //
            // If the type found does not implement IMethodImpl, then the 
            // wrong type was guessed. Stall with an exception immediately
            // to stop any attempt to use the type.
            //

            if (!typeof(IMethodImpl).IsAssignableFrom(implType))
            {
                throw new MemberImplException(
                    string.Format("{0} has no corresponding implementation provider type.",
                    attributeName));
            }

            return implType;
        }
    }
}
